home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / SPRANIM.ZIP / SPRANIM.TXT
Encoding:
Internet Message Format  |  1996-04-26  |  15.4 KB

  1. From umcaner0@cc.umanitoba.ca Wed Sep 14 20:16:09 1994
  2. Subject: A method for sprite animation (long)
  3. Date: 13 Sep 1994 23:01:51 GMT
  4.  
  5.  
  6. Hi, what follows is a text file I wrote describing a method of
  7. sprite animation I am using in a game.  I hope people find it
  8. informative.
  9.  
  10. A SIMPLE METHOD FOR SPRITE ANIMATION
  11.  
  12. By Darren Gyles
  13. * [Shameless Plug] 
  14.   Author of HOOP 
  15.   ( Avaliable at  ftp.funet.fi /pub/msdos/games/cards/hoop11.zip )
  16.  
  17. Note : The following implementation of sprite animation uses linked lists
  18. fairly extensively.  If you are not familiar with this concept, get any
  19. first year computer science textbook and look into it.
  20.  
  21. I have recently been programming a simple Galaxian type shoot-em-up and
  22. needed a method for sprite animation.  This is the solution that I came up
  23. with.  I was fairly pleased with the results.  I'm sure that this method is 
  24. not unique, but I haven't seen anybody mention it before so I will take
  25. a stab at it.
  26.  
  27. I am assuming that you already have your sprite graphics routines written.
  28. I will not cover machine specific graphics routines, just algorithms for 
  29. implementing sprites. 
  30.  
  31. This document describes a simple sprite system and then expands this
  32. system to include animation.  Be forewarned that even though there is
  33. quite a bit of code given, you will never get it to work unless you
  34. understand the methods completely.  It is really not very hard, the 
  35. most confusing thing are the linked lists.  Good luck.
  36.  
  37.  
  38. THE BASIC IDEA BEHIND SPRITE ANIMATION
  39.  
  40. The basic idea behind sprite animation (or any animation, for that matter) 
  41. is:
  42.  
  43.      Show frame 1 -> wait delay period -> Show frame 2 -> delay -> etc..
  44.  
  45. Pretty simple, but how do you implement this?
  46.  
  47.  
  48.                           - IMPLEMENTATION -
  49.  
  50. GENERAL SPRITE STUFF
  51.  
  52. For this discussion I will assume you have a linked list of sprites, and
  53. each node in the list represents a sprite and contains all the information
  54. relevant to that sprite (of course you could use an array for this as well
  55. with some simple modifications).  I am using C type pseudo code - I hope
  56. that everybody can understand it.
  57.  
  58. Each node may look like:
  59.  
  60. struct sprite {
  61.  
  62.    int      xpos, ypos;     // X and Y coordinates for sprite
  63.    int      xspeed, yspeed; // X and Y speed of sprite
  64.    int      type;           // Type of sprite (ie 1 = ship, 2 = bullet etc..)
  65.                             // These types will be defined by you
  66.    char     background[256];
  67.                     // Stores the piece of the background 
  68.                     // that this sprite covers
  69.    .
  70.    .    // Various other parameters describing the sprite that you may 
  71.    .    // require.
  72.    .    // (ie does the sprite collide with other sprites?,
  73.    .    //     does the sprite bounce around or just disappear at edge of
  74.    .    //     the screen?  etc.. )
  75.    .
  76.    struct sprite  *next;  // Pointer to next sprite (NULL means end of list)
  77.  
  78. };
  79.  
  80. And you will have a pointer to the top of this list:
  81.  
  82.     struct sprite *sprite_list;
  83.  
  84. So you will have a list that looks like this:
  85.  
  86. sprite_list --> Sprite Data    /----> Sprite Data     /-->NULL
  87.                 Next ---------/       Next  ---------/
  88.  
  89.  
  90. NOTE: For simplicity, I am assuming that all sprites are the same size.
  91. Which is a dumb assumption, but it should be pretty easy to alter
  92. these functions to work with variable sized elements.
  93.  
  94. You should have an add_sprite() function and a delete_sprite() function 
  95. (which are not given here, but are pretty simple) which add and delete
  96. elements to and from the linked list.  And you will have a function that 
  97. looks like this:
  98.  
  99. // This function goes through the sprite list and updates all sprites
  100. void do_sprites(void) {
  101.  
  102.    struct sprite *current;
  103.  
  104.    current = sprite_list;
  105.  
  106.    while (current != NULL) {
  107.  
  108.       // This function restores the background, by placing the 
  109.       // data pointed to by current->background at current->xpos,
  110.       // current->ypos.  You write this one.
  111.       RESTORE_BACKGROUND(current->background, current->xpos, current->ypos);
  112.       
  113.       // Update x and y coordinates
  114.       current->xpos = current->xpos + current->xspeed;
  115.       current->ypos = current->ypos + current->yspeed;
  116.  
  117.       // Do whatever bounds checking or whatever you want here
  118.  
  119.       // This function will make a copy of the background that will be 
  120.       // covered by the sprite in the array pointed to by 
  121.       // current->background. You have to write this one too.
  122.       CUT_BACKGROUND(current->background, current->xpos, current->ypos);
  123.  
  124.       // Draw sprite - This function draws the sprite at xpos, ypos
  125.       // the current->type would indicate what data to use when drawing.
  126.       // You'll have to write this one also.
  127.       // Uses the sprite_data[] array which contains the bitmaps for the
  128.       // sprites, see below for more details.
  129.       DRAWSPRITE(sprite_data[current->type], current->xpos, current->ypos);
  130.  
  131.       current = current->next;
  132.    }
  133. }
  134.  
  135. You would call this function every time you want to move the sprites (ie
  136. every time you go through your main loop).  Note that this is very simple
  137. and you don't have to do it this way, I am only providing this example
  138. to make the discussion of the animation more understandable.
  139.  
  140. To draw the sprite, you send the drawing function the address where the 
  141. sprite data starts. This data would be stored in an array that looks 
  142. something like:
  143.  
  144. unsigned char sprite_data[MAX_NO_OF_SPRITES][MAX_SIZE_OF_SPRITES];
  145.  
  146. So the data for sprite type 10 starts at sprite_data[10][0]. Remember that
  147. I am assuming all sprites are the same size. 
  148.  
  149. So, when do we animate?  Right now.
  150.  
  151.  
  152. ANIMATION
  153.  
  154. For animation I use linked lists.  You could use arrays instead, but 
  155. I like linked lists because they can be referenced faster and more neatly.
  156. The downside is that they are a little messy to set up and to deallocate when
  157. finished.
  158.  
  159. Each particular sprite animation has its own linked list and each node
  160. in that list represents a frame.  Each node has a structure that looks like:
  161.  
  162. struct frame{
  163.   unsigned char *data;  // Pointer to the bitmap data for this frame.
  164.                         // If you are using compiled bitmaps, you could put
  165.                         // a pointer to the compiled bitmap function here.
  166.   
  167.   int           delay;  // This value indicates how long to wait before
  168.                         // going to the next frame.
  169.  
  170.    struct frame *next;  // Pointer to the next frame
  171. }
  172.  
  173. So a sprite that just does the same thing over and over again.  Say an
  174. enemy ship that has blinking lights would have a linked list that looks
  175. like :
  176.  
  177. /-->   FRAME 1      ---->  FRAME2         -----\
  178. |      (Lights on)         (Lights off)        |
  179. |                                              |
  180. |                                              |
  181.  \---------------------------------------------/
  182.  
  183. Note that this animation only has two frames, but you could have as many as
  184. you like.  Also note that each frame has its own delay time so you can 
  185. make some frames appear for longer than others.
  186.  
  187.  
  188. Now this is where linked lists get a little messy, to create a linked
  189. list that had the above format it would look like:
  190.  
  191.    struct frame *current, *last;
  192.  
  193.    ...
  194.    
  195.    current        = allocate( sizeof(struct frame) );
  196.    // We store the pointer to the start of the animation in the array
  197.    // ANIMATIONS, see below for more details.
  198.    animations[0]  = current;                      
  199.    current->data  = POINTER_TO_FRAME1_DATA;
  200.    current->delay = 20;
  201.    last           = current;
  202.  
  203.    current        = allocate( sizeof(struct frame) );
  204.    current->data  = POINTER_TO_FRAME2_DATA;
  205.    current->delay = 20;
  206.    current->next  = last;
  207.    last->next     = current;
  208.  
  209.    ...
  210.  
  211.  
  212. Now assuming that we have several animation lists set up, how
  213. do we keep track of them?  Simply use an array.  We can use our TYPE
  214. field to index this array.  For example:
  215.  
  216. struct frame *animations[MAX_ANIMATIONS];
  217.  
  218. So say that element 0 in the animations[] array is a ship and element
  219. 1 is a bullet, then the array would look like:
  220.  
  221.     animations[0] --> Animation linked list for ship.
  222.     animations[1] --> Animation linked list for bullet.
  223.  
  224. So suppose we are creating a sprite that has type 4.  We know that the 
  225. animation starts at animations[4].
  226.  
  227.  
  228. Now we must add a couple of fields to the sprite struct that was defined 
  229. a little earlier in this document to help with our animation.  
  230. These fields are:
  231.  
  232. struct sprite {
  233.    ...
  234.    struct frame *a_frame;   // The animation frame that we are currently on
  235.    int          a_count;    // A counter that keeps track of the delay
  236.    ...
  237. };
  238.  
  239. So for each sprite we have these extra two fields.  Now when we are adding
  240. a sprite to the sprite_list, in addition to initializing xpos, ypos, xspeed,
  241. yspeed etc..  we have the following two lines:
  242.  
  243.    new_sprite->a_frame = animations[new_sprite->type];
  244.    new_sprite->a_count = new_sprite->a_frame->delay;
  245.  
  246. So for each sprite, we now will know what animation frame we are currently 
  247. on and how long we have left on this frame.
  248.  
  249. Each time we update the sprite, we subtract one from the a_count value.
  250. When a_count == 0, we move to the next frame.  
  251.    
  252.  
  253. Here is a new version of do_sprites() using animation:
  254.  
  255. void do_sprites(void) {
  256.  
  257.    struct sprite *current;
  258.  
  259.    current = sprite_list;
  260.  
  261.    while (current != NULL) {
  262.  
  263.       RESTORE_BACKGROUND(current->background, current->xpos, current->ypos);
  264.  
  265.       // NEW PART STARTS HERE
  266.       current->a_count--;
  267.       if (current->a_count ==0) {
  268.          // If we have reached end of animation, move to next frame and
  269.          // reset a_count.
  270.          current->a_frame = current->a_frame->next;
  271.          current->a_count = current->a_frame->delay;
  272.       }
  273.       // NEW PART ENDS HERE
  274.  
  275.       current->xpos = current->xpos + current->xspeed;
  276.       current->ypos = current->ypos + current->yspeed;
  277.  
  278.       // Do whatever bounds checking or whatever you want here
  279.  
  280.       CUT_BACKGROUND(current->background, current->xpos, current->ypos);
  281.       DRAWSPRITE(sprite_data[current->type], current->xpos, current->ypos);
  282.  
  283.       current = current->next;
  284.    }
  285. }
  286.  
  287. Once you have set up the animation linked list and added the sprite to 
  288. the sprite linked list, every time you call do_sprites the animation is done
  289. automatically.
  290.  
  291.  
  292. FINITE ANIMATIONS
  293.  
  294. Finite animations are animations that don't have a loop, that is, they end.
  295. Say for example an explosion, may have three frames: A small explosion,
  296. a bigger explosion and the final biggest explosion.  Once this animation 
  297. has ended we want to delete the explosion from sprite_list.  This is easy
  298. to do.
  299.  
  300.  
  301. The animation linked list for the described explosion would look like:
  302.  
  303.   EXPLOSION  ----->  EXPLOSION  -----> EXPLOSION -----> NULL
  304.   FRAME # 1          FRAME # 2         FRAME # 3
  305.  
  306.  
  307. The NEXT field of the last frame will be a NULL pointer.  So when you are
  308. advancing an animation, simply check to see if the next frame if NULL, if
  309. it is, then delete the sprite.
  310.  
  311. This alteration would look like:      
  312.  
  313. void do_sprites(void) 
  314. {
  315.    ...
  316.       
  317.       current->a_count--;
  318.       if (current->a_count ==0) {
  319.          // If we have reached end of animation, move to next frame and
  320.          // reset a_count.
  321.          if (current-a_frame->next == NULL)
  322.             // This function deletes the sprite from sprite_list
  323.             delete_sprite(current);
  324.          else {
  325.             current->a_frame = current->a_frame->next;
  326.             current->a_count = current->a_frame->delay;
  327.          }
  328.       }
  329.  
  330.    ...
  331. }
  332.  
  333.  
  334. Two things I must mention:
  335.    1. I didn't bother to show it in the above code fragment, but if you
  336.       delete the sprite, make sure that you jump over the following lines
  337.       that update the x and y positions and draw the sprite.  Since the node
  338.       no longer exists, these operations are no longer valid.
  339.  
  340.    2. This has nothing to do with sprites, but it is important to
  341.       realize that on MS-DOS machines comparing two pointers will
  342.       not always work because of the segmented architecture. 
  343.  
  344.       This is important when deleting a node.  For example suppose
  345.       CURRENT is the node to be deleted, and LAST is the node directly
  346.       before CURRENT in the list.  To delete CURRENT we would do the 
  347.       following:
  348.  
  349.           last->next = current->next;
  350.           free(current);
  351.  
  352.       So we need to know which node precedes the node to be deleted.
  353.       Since the nodes don't keep track of the previous node (only the next) 
  354.       we must traverse the list and find the node that precedes the node 
  355.       to be deleted.  But since we cannot compare pointers, we cannot simply 
  356.       do the following: (current is a pointer to node to be deleted)
  357.  
  358.          last = sprite_list;
  359.          while (last->next != current)
  360.             last = last->next
  361.  
  362.       What I did was to give each node a unique nodeid, so to delete
  363.       a node, do the following:
  364.  
  365.          last = sprite_list;
  366.          while (last->next->nodeid != current->nodeid)
  367.             last = last->next
  368.  
  369.       This version would work.  Remember that each node must have a UNIQUE 
  370.       nodeid value.  I hope this makes sense,  if you are familiar with
  371.       linked lists it should.  Although this isn't directly related to 
  372.       sprite animation you must understand this problem if you wish to
  373.       implement the system as described in this document.
  374.  
  375.  
  376. CLEANING UP THE ANIMATIONS
  377.  
  378. When the program is done executing, we must deallocate all the memory
  379. that is used up by the animation linked lists.  As I mentioned earlier, 
  380. this is a little messy, but not too bad. (Also remember to deallocate all 
  381. the nodes in the sprite_list linked list.  This is very easy to do.)
  382.  
  383. Remember that all the animations are pointed to by the array called
  384. (not surprisingly) animations[].  For each element in this array, we 
  385. delete the animation that is pointed to.
  386.  
  387. This is pretty simple, except that some of the animations have loops in them
  388. so we cannot simply do the following.
  389.  
  390.    struct frame *current, *next;
  391.    
  392.    current = animations[0];
  393.    while (current!=NULL) {
  394.       next = current->next;
  395.       free(current);
  396.       current=next;
  397.     }
  398.  
  399. What we must do is first check to see if the list is finite.  So what I do 
  400. is follow the list for 10 frames and see if a NULL pointer is found, if we
  401. find one, the list does not loop and we can use the above method.  If we 
  402. don't find an end, (and we are assuming that no animations have 10 or
  403. more frames) then we "break" the loop, turning the animation int a non-
  404. looped list and delete the animation as above. This "breaking" looks
  405. like:
  406.     
  407.    struct frame *current, *next;
  408.    
  409.    // Break the loop
  410.    next = animations[0];
  411.    current = next->current
  412.    next->next = NULL;
  413.  
  414.    // Deallocate list
  415.    while (current!=NULL) {
  416.       next = current->next;
  417.       free(current);
  418.       current=next;
  419.     }
  420.  
  421. It is good practice to always deallocate all dynamic memory before 
  422. terminating your program, so always try to do it, even if it is a pain.
  423.  
  424.  
  425. CONCLUSION
  426.  
  427. Well, that's pretty much it.  The basic idea is pretty simple, but I 
  428. complicated it with all the implementation details.  I hope this makes 
  429. sense to people.  
  430.  
  431. If you have any questions/comments/criticisims please feel free to E-mail 
  432. me at umcaner0@ccu.umantioba.ca
  433.  
  434. Also feel free to send me any programs you write that uses this technique,
  435. I would like to see if anybody can actually decipher this mess and 
  436. get something working.
  437.  
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444.